use axum::body::Body;
use axum::http::{Request, Response};
use axum::response::IntoResponse;
use axum::response::Redirect;
use std::{
    future::Future,
    pin::Pin,
    task::{Context, Poll},
};
use tklog::info;

use crate::session::session_manage::SessionManager;

/// a demo https://tower-rs.github.io/tower/tower_layer/trait.Layer.html
use tower_layer::Layer;
use tower_service::Service;

#[derive(Clone)]
pub struct SessionLayer<'a> {
    session_manager: &'a SessionManager,
}

impl<'a> SessionLayer<'a> {
    pub fn new(session_manager: &'a SessionManager) -> Self {
        SessionLayer { session_manager }
    }
}

impl<S> Layer<S> for SessionLayer<'_> {
    type Service = SessionService<S>;

    fn layer(&self, service: S) -> Self::Service {
        SessionService {
            service,
            session_manager: self.session_manager.clone(),
        }
    }
}

// This service implements the Log behavior
#[derive(Debug, Clone)]
pub struct SessionService<S> {
    service: S,
    session_manager: SessionManager,
}

impl<S> Service<Request<Body>> for SessionService<S>
where
    S: Service<Request<Body>, Response = Response<Body>>,
    S::Future: Send + 'static,
    S::Response: Send + 'static,
{
    type Response = Response<Body>;
    type Error = S::Error;
    type Future =
        Pin<Box<dyn Future<Output = Result<Self::Response, Self::Error>> + Send + 'static>>;

    fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
        self.service.poll_ready(cx)
    }

    fn call(&mut self, req: Request<Body>) -> Self::Future {
        let uri = req.uri().path();
        info!("Request URI: {:?}", uri);
        if uri == "/toupload"
            || uri == "/market/management"
            || uri == "/market/stock/index"
            || uri == "/market/stock/news"
        {
            let cookies = req.headers().get("cookie");
            if let None = cookies {
                let response = Redirect::temporary("/tologin");
                return Box::pin(async { Ok(response.into_response()) });
            }
            println!("the cookies:{:?}", cookies.unwrap().to_str());
            let cookies = cookies.unwrap().to_str().unwrap();
            let should_redirect = if cookies.contains("sessionid") {
                let session_id = cookies
                    .split(';')
                    .map(|s| s.trim())
                    .find(|s| s.starts_with("sessionid="))
                    .and_then(|s| s.split_once('='))
                    .map(|(_, v)| v)
                    .unwrap();
                info!("the sessionid:{:?}", session_id);

                if self.session_manager.is_session_exist(&session_id) {
                    if self.session_manager.is_expired(&session_id) {
                        let _ = self.session_manager.remove_session(&session_id);
                        true
                    } else {
                        info!("Session is valid");
                        false
                    }
                } else {
                    info!("Session does not exist");
                    true
                }
            } else {
                info!("No session found");
                true
            };
            if should_redirect {
                info!("Returning response");
                let response = Redirect::temporary("/tologin");
                return Box::pin(async { Ok(response.into_response()) });
            };
        }
        Box::pin(self.service.call(req))
    }
}
